/* *****************************************************************
 *  库文件、头文件
 * *****************************************************************/
#include <ArduinoJson.h>  //json库
#include <Button2.h>      //按钮库
#include <HTTPClient.h>   //HTTP库
#include <PubSubClient.h> //MQTT库
#include <SPI.h>
#include <StaticThreadController.h> //协程控制
#include <TFT_eSPI.h>    //屏幕库
#include <TJpg_Decoder.h>
//使用Tjpg_Decoder库创建C数组图片的网址: http://tomeko.net/online_tools/file_to_hex.php?lang=en 
#include <Thread.h>       //协程
#include <TimeLib.h>      //时间库
#include <WebServer.h>    //Web服务器
#include <WiFi.h>         //WiFi库
#include <WiFiUdp.h>      //UDP库
#include <SetWiFi.h>      //配网文件
#include <wifiClient.h>   //WiFi客户端
#include "Animate/Animate.h" //动画模块
#include "config.h"          //配置文件
#include "weatherNum/weatherNum.h" //天气图库
#include "misspage/miss.h"        //想你页面
#include "audiopage/audiopage.h"//对讲机页面
#include "IRControl/IRControl.h"//红外页面
#include "AudioMqtt/AudioMqtt.h"   //对讲机mqtt
#include "IISAudio/IISAudio.h"     //I2S音频
#include "generated/gui_guider.h"  //LVGL库
#include "custom/custom.h"         //lvgl自定义库
#include "generated/events_init.h" //lvgl事件库
#include "lvgl.h"                  //lvgl库
#include "Animate/img/audio.h"     //音频图标
#include "img/IR_ICON.h"           //红外图标
#include "img/zhendong.h"          //震动图标
#include "misspage/img/wifistate.h" //wifi图标
#include "esp_task_wdt.h"          //看门狗
#include "freertos/FreeRTOS.h"     //FreeRTOS
#include "freertos/task.h"         //FreeRTOS
#include "freertos/stream_buffer.h"//FreeRTOS
#include "Animate/img/image_miss.h"//想你页面动画

#define CLOCK_PAGE 0            //时钟页面代码
#define LOVE_PAGE 1             //纪念日页面代码
#define MISSU_PAGE 2            //想你页面代码
#define AUDIO_PAGE3 3           //准备进入对讲机页面代码
#define I2S_PAGE 4              //对讲机页面代码
#define IR_PAGE5 5              //红外页面代码
/* *****************************************************************
 *  配置使能位
 * *****************************************************************/

// 按钮定义
Button2 Button_right = Button2(12); // 右按钮
Button2 Button_left = Button2(16);  // 左按钮 控制想你页面按键
Button2 Button_middle = Button2(23);  // 中按钮 控制想你页面按键
/* *****************************************************************
 *  字库、图片库
 * *****************************************************************/
#include "font/Chancery_L_20.h" //字体库
#include "font/lonely20.h"      //字体库
#include "font/ZdyLwFont_20.h"  //字体库
#include "font/timeClockFont.h" //字体库
#include "font/heiti12.h"       //字体库
#include "font/ARLRDBD28.h"     //字体库
#include "img/humidity.h"       //湿度图标
#include "img/temperature.h"    //温度图标
#include "img/battery.h"        //电池图标
#include "img/setWiFi_img.h"    //配网图标
/* *****************************************************************
 *  函数声明
 * *****************************************************************/

// 按键响应函数
void leftlongclick(Button2 &btn);         // 左键长按响应
void rightlongclick(Button2 &btn);        // 右键长按响应
void middlesingleclick(Button2 &btn);     // 中键单击响应
void middlelongclick(Button2 &btn);       // 中键长按响应
void middledoubleclick(Button2 &btn);     // 中键双击响应
void leftsingleclick(Button2 &btn);       // 左键单击响应
void leftdoubleclick(Button2 &btn);       // 左键双击响应
void rightsingleclick(Button2 &btn);      // 右键单击响应
void rightdoubleclick(Button2 &btn);      // 右键双击响应
void leftreleased(Button2 &btn);          // 左键释放响应
void leftpressed(Button2 &btn);           // 左键按下响应
void middlereleased(Button2 &btn);        // 中键释放响应
void middlepressed(Button2 &btn);         // 中键按下响应
void rightpressed(Button2 &btn);          // 右键按下响应
void rightreleased(Button2 &btn);         // 右键释放响应
void buttonHandler(Button2& btn);         // 按键处理函数
void setup_button();                      // 初始化所有按钮

// 时间和NTP相关函数
// 星期
String week() {
  String wk[7] = {"日", "一", "二", "三", "四", "五", "六"};
  String s = "周" + wk[weekday() - 1];
  return s;
}
// 月日
String monthDay() {
  String s = String(month());
  s = s + "月" + day() + "日";
  return s;
}
void sendNTPpacket(IPAddress &address);   // 向NTP服务器发送请求
time_t getNtpTime();                      // 从NTP获取时间
void digitalClockDisplay(int reflash_en); // 数字时钟显示

// 屏幕和显示相关函数
void loadingTask(void *pvParameters);    // 加载页面
void clear_screen();                      // 清屏
void screenTask();                        // 自动熄屏任务
void scrollBanner();                      // 时钟页面信息滚动
void refresh_AnimatedImage();             // 更新时钟页面右下角动画
void goto_clock_page();                   // 跳转到时钟页面
void Battery();                           // 电压检测
void update_onlineStatus();               // 更新在线状态
void change_gif();                        // 更换时钟页面GIF

// WiFi和网络相关函数
void loadWiFiInfo();                      // 从eeprom读取WiFi信息ssid，psw
void deleteWiFiConfig();                  // 删除原有eeprom中的信息
void wifi_reset();                        // 重置WIFI设置
void setWiFi();                           // 配网

// 天气相关函数
void getCityWeather();                    // 获取城市天气
void weaterData(String *cityDZ, String *dataSK, String *dataFC); // 天气信息写到屏幕上

// 纪念日和情话相关函数
unsigned int get_love_day(time_t start_love);// 获取计算恋爱天数
void get_honeyed_words();                    // 获取新的情话
void show_honey_words(String *words);        // 显示情话
void goto_love_page();                       // 跳转到纪念日页面

// MQTT和"想你"页面相关函数
void mqtt_reconnect();                                                    // mqtt重连
void mqttcallback(char *topic, byte *payload, unsigned int length);       // 想你信号mqtt回调
void callback(char *topic, byte *payload, unsigned int length);           // 所有mqtt消息回调
void mqttcallback_audio(char *topic, byte *payload, unsigned int length); // 请求对话回调
void mqttcallback_status(char *topic, byte *payload, unsigned int length);// 连接状态回调
void goto_miss_page(String word, uint16_t word_color);                   // 页面跳转
void refresh_AnimatedMiss();                                              // 播放想你页面GIF动画

// 对讲机和音频相关函数
void goto_I2S_page();                     // 进入对讲机页面
void exit_I2S_page();                     // 退出对讲机页面
void goto_audio_page();                   // 切换到audio页面
void setup_I2S_lvgl();                    // 初始化I2S的lvgl页面
void update_volume_bar(int volume);       // 更新音量条
void refresh_AnimatedIcon();              // 播放audio3页面GIF动画
void ringBell();                          // 启用震动

// 红外相关函数
void goto_IR_page();                      // 跳转红外页面
void enter_IR_page();                     // 进入红外页面并初始化
void exit_IR_page();                      // 退出红外页面并卸载模块

// LVGL相关函数
void imgbtn_event_cb(lv_obj_t * obj, lv_event_t event);                             // 处理按键回调
void update_focused_item(lv_ui *ui, int infraredDataIndex);                         // 更新红外页面正在选取的条目
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);// 刷新屏幕
void lv_setup_AUDIO_PAGE_chart_2(lv_ui *ui);                                        // 初始化图表

// FreeRTOS任务相关函数
void Supervisor_listTask_controller();    // 定期开启对讲机list图表刷新标志
void deleteTasks();                       // 删除frreerots任务
void updateChartTask(void *parameter);    // 更新对讲机图表任务
void audioProcessingTask(void *parameter);// 对讲机发送音频数据任务

// 通用页面导航函数
void goto_page(byte page);                // 跳转到指定页面
//*********************时钟线程****************************
// 创建时间更新函数线程
Thread reflash_time = Thread();
// 创建副标题切换线程
Thread reflash_Banner = Thread();
// 创建联网获取信息线程
Thread reflash_DataFromNetwork = Thread();
// 创建播放动画线程
Thread reflash_Animate = Thread();
// 电池电压检测
Thread reflash_Battery = Thread();
// 创建协程池
StaticThreadController<5> controller(&reflash_time, &reflash_Banner, &reflash_DataFromNetwork, &reflash_Animate, &reflash_Battery);

//********************动画icon线程******************************
// 创建播放audio3页面动画线程
Thread reflash_icon = Thread();
// 创建播放miss页面动画线程
Thread reflash_miss = Thread();

//********************lvgl线程******************************
// 创建lvgl线程
Thread lvgl_thread = Thread();
Thread list_thread = Thread();

//********************屏幕线程******************************
// 创建屏幕熄灭线程
Thread screen_thread = Thread();
/* *****************************************************************
 *  参数设置
 * *****************************************************************/

WiFi_Info wifi_info_list[5];  // 支持最多存储5组WiFi信息
const int wifi_info_count = sizeof(wifi_info_list) / sizeof(WiFi_Info);
bool setWiFi_Flag = false;      //是否已经设置wifi标志位
bool wifiScanComplete = false; // 用于标志 WiFi 扫描是否完成
String cachedSSIDs[15];  // 缓存的SSID
int cachedSSIDCount = 0;       // 缓存的SSID数量
unsigned long secsSince1900 = 0; // 时钟信息
const char* currentVersion = "1.0"; // 当前版本号
// 天气信息
struct Weather_Msg {
  String cityDZ;
  String dataSK;
  String dataFC;
};

Weather_Msg weather_msg = {{""}, {""}, {""}}; //天气信息
//----------------------------------------------------

// LCD屏幕相关设置
bool screenOff = false;            //屏幕是否亮起标志
bool screenOff_flag = true;        //屏幕熄灭标志
unsigned long lastActivityTime = 0;//屏幕亮起时长

TFT_eSPI tft = TFT_eSPI();
TFT_eSprite clk = TFT_eSprite(&tft);
TFT_eSprite sdm = TFT_eSprite(&tft);
TFT_eSprite clkb = TFT_eSprite(&tft);

uint16_t bgColor =  0xfe3a;                       //设置自定义粉主背景颜色
uint16_t Main_descr = 0xd497;                     //设置自定义紫色颜色
uint16_t zongseColor = tft.color565(128, 64, 64); //设置自定义棕色颜色
uint16_t whiteColor = tft.color565(245, 246, 247);//设置自定义颜色
uint16_t hourColor = 0x93f5;                      //设置小时颜色   
uint16_t minColor = 0xdddc;                       //设置分钟颜色       
// 其余状态标志位
int LCD_Rotation = 4;                             // LCD屏幕方向
int brightness = 255;                         // 屏幕亮度
int prevTime = 0;                                 // 滚动显示更新标志位
time_t love_start_time = 0;
int Mode = 1;                                     // 时钟页面的动画选择

time_t prevDisplay = 0;                           // 显示时间显示记录
int Amimate_reflash_Time = 0;                     // 更新时间记录
int connectTimes = 0;                             // 尝试WiFi连接次数

/*** Component objects ***/
WeatherNum wrat;

uint32_t targetTime = 0;
int tempnum = 0;               // 温度百分比
int huminum = 0;               // 湿度百分比
int tempcol = minColor;        // 温度显示颜色
int humicol = 0x14bb;          // 湿度显示颜色

// NTP服务器参数
static const char ntpServerName[] = "ntp6.aliyun.com";
//static const char ntpServerName[] = "pool.ntp.org"; // 备用NTP服务器地址
const int timeZone = 8; // 东八区

// wifi连接UDP设置参数
WiFiUDP Udp;
WiFiClient wifiClient;         // WiFi客户端
unsigned int localPort = 8000; // 本地端口
bool wifi_state = false;       // WiFi连接状态


// MQTT 参数
PubSubClient mqttClient(wifiClient);
bool send_flag = true;
String mqtt_flag = "unknow";

// 显示页面参数
byte page = CLOCK_PAGE;
byte pre_page = CLOCK_PAGE;
byte role = RECEIVER;
byte role_1 = RECEIVER;
String words = DEFAULT_WORDS;

// 时间相关函数
String week();
String monthDay();

// 对讲机参数
extern const uint16_t* images[];
bool currentBtnState;
bool sendOver = true;
bool recOver = false;
bool speakOut;
bool previousBtnState = false;
bool micInitialized = false;
float volume = 5.0;
bool I2S_STATE = false;
extern int16_t *recive_16bit_list;
char play_ye = 0;
bool play_flag = false;
extern int16_t *samples_16bit;
bool ring_state = true;

// 红外参数
bool IR_CHOICE = false;
int DataIndex = 0;

// LVGL 参数
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
lv_ui guider_ui;
lv_chart_series_t * AUDIO_PAGE_chart_2_0;
bool icon_lastState = false;
bool list_refresh_falg = false;
bool list_refresh_falg_send = false;

// 连接状态参数
bool onlineStatus = false;
bool appStatus = false;

// FreeRTOS 任务句柄
TaskHandle_t audioTaskHandle = NULL;
TaskHandle_t chartTaskHandle = NULL;
TaskHandle_t mqttTaskHandle = NULL;
TaskHandle_t mqttProcessingTaskHandle;
TaskHandle_t loadingTaskHandle = NULL; // 用于保存进度条任务的句柄

// 音频缓冲区相关
#define MAX_AUDIO_BUFFER_SIZE 1024
#define STREAM_BUFFER_SIZE (MAX_AUDIO_BUFFER_SIZE * 2)
StreamBufferHandle_t audioStreamBuffer;
static int16_t recive_16bit[MAX_AUDIO_BUFFER_SIZE];
static int16_t output_16bit[MAX_AUDIO_BUFFER_SIZE * 2];
/* *****************************************************************
 *  函数
 * *****************************************************************/

// 进度条函数
byte loadNum = 6;
void loading(byte delayTime){
  clk.setColorDepth(16);
  clk.createSprite(200, 100); // 创建窗口
  clk.fillSprite(0x0000);     // 填充率
  clk.drawRoundRect(0, 0, 200, 16, 8,bgColor);     // 空心圆角矩形
  clk.fillRoundRect(3, 3, loadNum, 10, 5, bgColor); // 实心圆角矩形
  clk.setTextDatum(CC_DATUM);                      // 设置文本数据
  clk.setTextColor(TFT_GREEN, 0x0000);
    if(!wifiScanComplete){
    clk.drawString("Search for WiFi......", 100, 40, 2);
  }
  else
  clk.drawString("Connecting to WiFi......", 100, 40, 2);
  clk.setTextColor(TFT_WHITE, 0x0000);
  clk.drawRightString(String("LoveLink UI ") + currentVersion, 180, 60, 2);
  clk.pushSprite(20, 120); // 窗口位置
  clk.deleteSprite(); //删除窗口
  loadNum += 1;
 delay(delayTime); // 使用 FreeRTOS 延迟
}



// 载入已储存WiFi函数
void loadWiFiInfo() {
  preferences.begin("wifi", true);
  for (int i = 0; i < wifi_info_count; i++) {
    String ssid = preferences.getString(("ssid" + String(i)).c_str(), "");
    String password = preferences.getString(("password" + String(i)).c_str(), "");
    cityCode = preferences.getString("citycode", "non");
    if (ssid.length() > 0) {
      strncpy(wifi_info_list[i].ssid, ssid.c_str(), sizeof(wifi_info_list[i].ssid));
      strncpy(wifi_info_list[i].password, password.c_str(), sizeof(wifi_info_list[i].password));
    }
  }
String devicename = preferences.getString("devicename", "");
strncpy(device_name, devicename.c_str(), sizeof(device_name) - 1);// 确保字符串以 null 结尾
device_name[sizeof(device_name) - 1] = '\0';
OwnerMode = preferences.getBool("gender", 0);
preferences.getString("mqtt_ip", MQTT_IP).toCharArray(MQTT_IP, sizeof(MQTT_IP));//如果没有新保存的则用config.c的的初值
preferences.getString("mqtt_user", MQTT_USER).toCharArray(MQTT_USER, sizeof(MQTT_USER));
preferences.getString("mqtt_pass", MQTT_PASSWORD).toCharArray(MQTT_PASSWORD, sizeof(MQTT_PASSWORD));
PORT = preferences.getUInt("mqtt_port", PORT);  
  preferences.end();
  Serial.println("port0:");
  Serial.println(PORT);
              for (; loadNum< 80;) {
              loading(30);
              }
    
}

//SSID转义
String sanitizeSSID(String ssid) {
  ssid.replace("&", "&amp;");
  ssid.replace("<", "&lt;");
  ssid.replace(">", "&gt;");
  return ssid;
}

//尝试WiFi连接函数
void tryConnectWiFi() {
      // 创建进度条任务，绑定到核心 0
  Serial.println("wifiSTA启动");
  WiFi.mode(WIFI_STA);
  bool connected = false;
  IPAddress dns(114, 114, 114, 114); // DNS
  // 扫描可用的 WiFi 网络
  Serial.println("扫描可用的 WiFi 网络...");
  int networkCount = WiFi.scanNetworks();
  wifiScanComplete = true;
  Serial.println("WiFi 扫描完成！");
  if (networkCount > 0) {
    cachedSSIDCount = min(networkCount, 15); // 限制缓存的SSID数量
    for (int i = 0; i < networkCount; i++) {
      String ssid = WiFi.SSID(i);
      Serial.print("找到网络: ");
      Serial.println(ssid);
      if(i < cachedSSIDCount){
    cachedSSIDs[i] = sanitizeSSID(WiFi.SSID(i));// 缓存SSID
      }
      // 检查是否与保存的 WiFi 信息匹配
      for (int j = 0; j < wifi_info_count; j++) {
        if (ssid == wifi_info_list[j].ssid) {
          Serial.print("尝试连接到匹配的网络: ");
          Serial.println(ssid);
          WiFi.begin(ssid.c_str(), wifi_info_list[j].password);
          int connectTimes = 0;
          while (WiFi.status() != WL_CONNECTED && connectTimes < 190) {
            for (byte n = 0; n < 10; n++) {
              loading(30);
              connectTimes++;
              if (WiFi.status() == WL_CONNECTED) {
                connected = true;
                break;
              }
            }
          }
          if (connected) {
            Serial.println("WiFi online!");
            Serial.print("IP address: ");
            Serial.println(WiFi.localIP());
            wifi_state = true;
            return;
          } else {
            Serial.println("连接失败。");
          }
        }
      }
    }
    
  }

  // 如果没有成功连接，使用原始方法尝试连接保存的 WiFi 信息
  if (!connected) {
    Serial.println("寻找有效内容");
    int lastValidIndex = -1;
    for (int i = 0; i < wifi_info_count; i++) {
      if (strlen(wifi_info_list[i].ssid) > 0) {
        lastValidIndex = i;
      }
    }

    for (int i = lastValidIndex; i >= 0; i--) {
      if (strlen(wifi_info_list[i].ssid) > 0) {
        Serial.print("正在连接 ");
        Serial.println(wifi_info_list[i].ssid);
        WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, dns);
        WiFi.begin(wifi_info_list[i].ssid, wifi_info_list[i].password);

        int connectTimes = 0;
        while (WiFi.status() != WL_CONNECTED && connectTimes < 190) {
          for (byte n = 0; n < 10; n++) {
            loading(30);
            connectTimes++;
            if (WiFi.status() == WL_CONNECTED) {
              connected = true;
              break;
            }
          }
        }

        if (connected) {
          Serial.println("WiFi online!");
          Serial.print("IP address: ");
          Serial.println(WiFi.localIP());
          return;
        } else {
          Serial.println("连接失败。");
          loadNum = 0;
        }
      }
    }
  }

  if (!connected) {

    Serial.println("启动配网模式...");
    setWiFi();
  }
}

// TFT屏幕输出函数
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
  if (y >= tft.height())
    return 0;
  tft.pushImage(x, y, w, h, bitmap);
  return 1;
}

//强制门户Web配网函数
void setWiFi() {  
  tft.setSwapBytes(true);
  tft.fillScreen(TFT_BLACK);
  tft.pushImage(0, 0, 240, 240, setWiFi_icon);
  tft.setSwapBytes(false);
  initBasic();
  initSoftAP();
  initWebServer();
  initDNS();
  while(setWiFi_Flag == false) {
    server.handleClient();
    dnsServer.processNextRequest();
    if(WiFi.status() == WL_CONNECTED) {
      server.stop();
      setWiFi_Flag = true;
    }
  }
}

// 湿度图标显示函数
void humidityWin() {
  clk.setColorDepth(16);   // 设置颜色深度
  clk.setSwapBytes(true);  // 设置字节交换（与颜色有关）
  huminum = huminum / 2;   // 湿度百分比
  clk.createSprite(52, 6); // 创建窗口
  clk.fillSprite(bgColor); // 填充率
  clk.drawRoundRect(0, 0, 52, 6, 3,Main_descr); // 空心圆角矩形  起始位x,y,长度，宽度，圆弧半径，颜色
  clk.fillRoundRect(1, 1, huminum, 4, 2, humicol); // 实心圆角矩形
  clk.pushSprite(45, 222);                         // 窗口位置
  clk.deleteSprite();
}

// 温度图标显示函数
void tempWin() {
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  clk.createSprite(52, 6); // 创建窗口
  clk.fillSprite(bgColor);  // 填充率
  clk.drawRoundRect(0, 0, 52, 6, 3,Main_descr); // 空心圆角矩形  起始位x,y,长度，宽度，圆弧半径，颜色
  clk.fillRoundRect(1, 1, tempnum, 4, 2, tempcol); // 实心圆角矩形
  clk.pushSprite(45, 192);                         // 窗口位置
  clk.deleteSprite();
}

// 删除已保存WiFi信息函数
void deleteWiFiConfig() {
    preferences.begin("wifi", false); // 打开 Preferences 进行写操作

    for (int i = 0; i < wifi_info_count; i++) {
        preferences.remove(("ssid" + String(i)).c_str());// 删除ssid
        preferences.remove(("password" + String(i)).c_str());// 删除password
    }
    preferences.remove("citycode"); // 删除citycode
    preferences.end(); // 结束 Preferences
    Serial.println("WiFi config deleted.");
}




// 获取城市天气函数
void getCityWeather() {
HTTPClient httpClient;// 创建 HTTPClient 对象
WiFiClient wifiClient;  
  String URL = "http://d1.weather.com.cn/weather_index/" + cityCode + ".html?_=" + String(now()); // 原来
  httpClient.begin(wifiClient, URL); 
  // 设置请求头中的User-Agent
  httpClient.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac "
                          "OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) "
                          "Version/11.0 Mobile/15A372 Safari/604.1");
  httpClient.addHeader("Referer", "http://www.weather.com.cn/");
  Serial.println(cityCode);
  // 启动连接并发送HTTP请求
  int httpCode = httpClient.GET();
  Serial.println("正在获取天气数据");
  Serial.println(URL);

  // 如果服务器响应OK则从服务器获取响应体信息并通过串口输出
  if (httpCode == HTTP_CODE_OK) {
    String str = httpClient.getString();
    int indexStart = str.indexOf("weatherinfo\":");
    int indexEnd = str.indexOf("};var alarmDZ");
    weather_msg.cityDZ = str.substring(indexStart + 13, indexEnd);
    indexStart = str.indexOf("dataSK =");
    indexEnd = str.indexOf(";var dataZS");
    weather_msg.dataSK = str.substring(indexStart + 8, indexEnd);
    indexStart = str.indexOf("\"f\":[");
    indexEnd = str.indexOf(",{\"fa");
    weather_msg.dataFC = str.substring(indexStart + 5, indexEnd);
    weaterData(&(weather_msg.cityDZ), &(weather_msg.dataSK), &(weather_msg.dataFC));
    Serial.println("获取成功");
  } else {
    Serial.println("请求城市天气错误：");
    Serial.print(httpCode);
  }

  // 关闭ESP32与服务器连接
  httpClient.end();
}

String scrollText[7];
// 天气信息写到屏幕上
void weaterData(String *cityDZ, String *dataSK, String *dataFC) {
  // 解析第一段JSON
  DynamicJsonDocument doc(1024);
  deserializeJson(doc, *dataSK);
  JsonObject sk = doc.as<JsonObject>();

  /***绘制相关文字***/
  clk.setColorDepth(16);
  clk.loadFont(ZdyLwFont_20);
  clk.setSwapBytes(true);
  // 温度
  clk.createSprite(58, 24);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(sk["temp"].as<String>() + "℃", 28, 13);
  clk.pushSprite(100, 184);
  clk.deleteSprite();
  tempnum = sk["temp"].as<int>();
  tempnum = tempnum + 10;
  tempWin();
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  // 湿度
  clk.createSprite(58, 24);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(sk["SD"].as<String>(), 28, 13);
  clk.pushSprite(100, 214);
  clk.deleteSprite();
  huminum = atoi((sk["SD"].as<String>()).substring(0, 2).c_str());
  humidityWin();
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  // 城市名称
  clk.createSprite(92, 30);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(sk["cityname"].as<String>(), 44, 16);
  clk.pushSprite(20, 15);
  clk.deleteSprite();
  // PM2.5空气指数
  uint16_t pm25BgColor = tft.color565(156, 202, 127); // 优
  String aqiTxt = "优";
  int pm25V = sk["aqi"];
  if (pm25V > 200) {
    pm25BgColor = tft.color565(136, 11, 32); // 重度
    aqiTxt = "重度";
  } else if (pm25V > 150) {
    pm25BgColor = tft.color565(186, 55, 121); // 中度
    aqiTxt = "中度";
  } else if (pm25V > 100) {
    pm25BgColor = tft.color565(242, 159, 57); // 轻
    aqiTxt = "轻度";
  } else if (pm25V > 50) {
    pm25BgColor = tft.color565(247, 219, 100); // 良
    aqiTxt = "良";
  }
  clk.setColorDepth(16);    // 设置颜色深度
  clk.setSwapBytes(true);   // 设置字节交换（与颜色有关）
  clk.createSprite(56, 24); // 创建窗口
  clk.fillSprite(bgColor);  // 设置背景颜色
  clk.fillRoundRect(0, 0, 50, 24, 4, pm25BgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(0x0000);
  clk.drawString(aqiTxt, 25, 13);
  clk.pushSprite(104, 18);  // 窗口位置
  clk.deleteSprite();       // 删除窗口

  scrollText[0] = "实时天气 " + sk["weather"].as<String>();
  scrollText[1] = "空气质量 " + aqiTxt;
  scrollText[2] = "风向 " + sk["WD"].as<String>() + sk["WS"].as<String>();
  // 天气图标
  wrat.printfweather(165, 5, atoi((sk["weathercode"].as<String>()).substring(1, 3).c_str()));
  // 左上角滚动字幕
  // 解析第二段JSON
  deserializeJson(doc, *cityDZ);
  JsonObject dz = doc.as<JsonObject>();
  scrollText[3] = "今日" + dz["weather"].as<String>();
  deserializeJson(doc, *dataFC);
  JsonObject fc = doc.as<JsonObject>();
  scrollText[4] = "最低温度" + fc["fd"].as<String>() + "℃";
  scrollText[5] = "最高温度" + fc["fc"].as<String>() + "℃";
  clk.unloadFont();
}

int currentIndex = 0;


// 时钟页面信息滚动函数
void scrollBanner() {
  if (scrollText[currentIndex]) {
    clkb.setSwapBytes(true);
    clkb.setColorDepth(16);
    clkb.loadFont(ZdyLwFont_20);
    clkb.createSprite(150, 30);
    clkb.fillSprite(bgColor);
    clkb.setTextWrap(false);
    clkb.setTextDatum(CC_DATUM);
    clkb.setTextColor(Main_descr, bgColor);
    clkb.drawString(scrollText[currentIndex], 74, 16);
    clkb.pushSprite(10, 45);
    clkb.deleteSprite();
    clkb.unloadFont();
    if (currentIndex >= 5)
      currentIndex = 0; // 回第一个
    else
      currentIndex += 1; // 准备切换到下一个
  }
  prevTime = 1;
}
// 用快速线方法绘制数字
void drawLineFont(uint32_t _x, uint32_t _y, uint32_t _num, uint32_t _size, uint32_t _color) {
  uint32_t fontSize;
  const LineAtom *fontOne;
  // 小号(9*14)
  if (_size == 1) {
    fontOne = smallLineFont[_num];
    fontSize = smallLineFont_size[_num];
    // 绘制前清理字体绘制区域
    tft.fillRect(_x, _y, 9, 14, bgColor);
  }
  // 中号(18*30)
  else if (_size == 2) {
    fontOne = middleLineFont[_num];
    fontSize = middleLineFont_size[_num];
    // 绘制前清理字体绘制区域
    tft.fillRect(_x, _y, 18, 30, bgColor);
  }
  // 大号(36*90)
  else if (_size == 3) {
    fontOne = largeLineFont[_num];
    fontSize = largeLineFont_size[_num];
    // 绘制前清理字体绘制区域
    tft.fillRect(_x, _y, 36, 90,bgColor);
  } else
    return;
  for (uint32_t i = 0; i < fontSize; i++) {
    tft.drawFastHLine(fontOne[i].xValue + _x, fontOne[i].yValue + _y, fontOne[i].lValue, _color);
  }
}

int Hour_sign = 60;
int Minute_sign = 60;
int Second_sign = 60;
// 日期刷新
void digitalClockDisplay(int reflash_en = 0) {
  // 时钟刷新,输入1强制刷新
  int now_hour = hour();     // 获取小时
  int now_minute = minute(); // 获取分钟
  int now_second = second(); // 获取秒针
  // 小时刷新
  if ((now_hour != Hour_sign) || (reflash_en == 1)) {
    drawLineFont(20, timeY, now_hour / 10, 3, hourColor);
    drawLineFont(60, timeY, now_hour % 10, 3, hourColor);
    Hour_sign = now_hour;
  }
  // 分钟刷新
  if ((now_minute != Minute_sign) || (reflash_en == 1)) {
    drawLineFont(101, timeY, now_minute / 10, 3, minColor);
    drawLineFont(141, timeY, now_minute % 10, 3, minColor);
    Minute_sign = now_minute;
  }
  // 秒针刷新
  if ((now_second != Second_sign) || (reflash_en == 1)) // 分钟刷新
  {
    drawLineFont(182, timeY + 30, now_second / 10, 2, hourColor);
    drawLineFont(202, timeY + 30, now_second % 10, 2, hourColor);
    Second_sign = now_second;
  }

  if (reflash_en == 1)
    reflash_en = 0;
  /***日期****/
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  clk.loadFont(ZdyLwFont_20);

  // 星期
  clk.createSprite(58, 30);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(week(), 29, 16);
  clk.pushSprite(102, 150);
  clk.deleteSprite();
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  // 月日
  clk.createSprite(95, 30);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(monthDay(), 49, 16);
  clk.pushSprite(5, 150);
  clk.deleteSprite();

  clk.unloadFont();
  /***日期****/
}

// 绘制两个圆形头像
/**
 * @brief 绘制两个圆形头像
 * @param x1, y1: 头像1圆心坐标
 * @param r1: 头像1半径
 * @param image1: 头像1图片数组
 * @param image1_width, image1_height: 头像1图片宽高
 * @param x2, y2: 头像2圆心坐标
 * @param r2: 头像2半径
 * @param image2: 头像2图片数组
 * @param image2_width, image2_height: 头像2图片宽高
 * @param bgColor: 矩形以内圆形以外的填充颜色
 **/
void drawTwoCircularImages(int x1, int y1, int r1, const uint16_t* image1, int image1_width, int image1_height,
                           int x2, int y2, int r2, const uint16_t* image2, int image2_width, int image2_height,
                           uint16_t bgColor) {
    // Draw first circular image
    tft.startWrite();
    tft.setAddrWindow(x1 - r1, y1 - r1, 2 * r1, 2 * r1);
    
    for (int j = -r1; j < r1; j++) {
        for (int i = -r1; i < r1; i++) {
            if (i*i + j*j <= r1*r1) {
                int imageX = (i + r1) * image1_width / (2 * r1);
                int imageY = (j + r1) * image1_height / (2 * r1);
                uint16_t pixel = image1[imageY * image1_width + imageX];
                tft.pushColor(pixel);
            } else {
                tft.pushColor(bgColor); // Fill with background color
            }
        }
    }

    tft.endWrite();
    tft.startWrite();
    tft.setAddrWindow(x2 - r2, y2 - r2, 2 * r2, 2 * r2);  
    for (int j = -r2; j < r2; j++) {
        for (int i = -r2; i < r2; i++) {
            if (i*i + j*j <= r2*r2) {
                int imageX = (i + r2) * image2_width / (2 * r2);
                int imageY = (j + r2) * image2_height / (2 * r2);
                uint16_t pixel = image2[imageY * image2_width + imageX];
                tft.pushColor(pixel);
            } else {
                tft.pushColor(bgColor); // Fill with background color
            }
        }
    }
    tft.endWrite();
}



/*-------- LVGL code ----------*/

void lv_setup_AUDIO_PAGE_chart_2(lv_ui *ui)
{
  ui->AUDIO_PAGE_chart_2 = lv_chart_create(ui->AUDIO_PAGE, NULL);
	lv_chart_set_type(ui->AUDIO_PAGE_chart_2, LV_CHART_TYPE_COLUMN);
	lv_chart_set_range(ui->AUDIO_PAGE_chart_2, 0, 100);
	lv_chart_set_div_line_count(ui->AUDIO_PAGE_chart_2, 0, 0);
	lv_chart_set_point_count(ui->AUDIO_PAGE_chart_2, 16);
	AUDIO_PAGE_chart_2_0 = lv_chart_add_series(ui->AUDIO_PAGE_chart_2, lv_color_hex(0xFFCC99));
	lv_obj_set_pos(ui->AUDIO_PAGE_chart_2, -4, 52);
	lv_obj_set_size(ui->AUDIO_PAGE_chart_2, 249, 144);
	//Set style for AUDIO_PAGE_chart_2. Part: LV_CHART_PART_BG, State: LV_STATE_DEFAULT
	static lv_style_t style_AUDIO_PAGE_chart_2_bg_default;
	lv_style_reset(&style_AUDIO_PAGE_chart_2_bg_default);
	lv_style_set_border_width(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, 1);
	lv_style_set_border_opa(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, 0);
	lv_style_set_border_color(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, lv_color_hex(0xe8e8e8));
	lv_style_set_radius(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, 0);
	lv_style_set_bg_opa(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, 0);
	lv_style_set_shadow_width(&style_AUDIO_PAGE_chart_2_bg_default, LV_STATE_DEFAULT, 0);
	lv_obj_add_style(ui->AUDIO_PAGE_chart_2, LV_CHART_PART_BG, &style_AUDIO_PAGE_chart_2_bg_default);
	//Set style for AUDIO_PAGE_chart_2. Part: LV_CHART_PART_SERIES_BG, State: LV_STATE_DEFAULT
	static lv_style_t style_AUDIO_PAGE_chart_2_series_bg_default;
	lv_style_reset(&style_AUDIO_PAGE_chart_2_series_bg_default);
	lv_style_set_line_color(&style_AUDIO_PAGE_chart_2_series_bg_default, LV_STATE_DEFAULT, lv_color_hex(0xe8e8e8));
	lv_style_set_line_width(&style_AUDIO_PAGE_chart_2_series_bg_default, LV_STATE_DEFAULT, 2);
	lv_style_set_line_opa(&style_AUDIO_PAGE_chart_2_series_bg_default, LV_STATE_DEFAULT, 255);
	lv_obj_add_style(ui->AUDIO_PAGE_chart_2, LV_CHART_PART_SERIES_BG, &style_AUDIO_PAGE_chart_2_series_bg_default);
}

void imgbtn_event_cb(lv_obj_t * obj, lv_event_t event)//按钮回调参数
{
    //Serial.printf("Event received: %d\n", event);
    switch(event) {
        case LV_EVENT_PRESSED:
            Serial.printf("checked\n");
           lv_imgbtn_set_state(obj, LV_BTN_STATE_PRESSED);
            // 这里可以添加按钮被按下时的其他操作
            break;
        case LV_EVENT_RELEASED:
            Serial.println("LV_EVENT_RELEASED received");
            lv_imgbtn_set_state(obj, LV_BTN_STATE_RELEASED);
            // 这里可以添加按钮被释放时的其他操作
            break;
    }
}

/* 显示刷新 */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors(&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}

// 更新焦点项目
void update_focused_item(lv_ui *ui, int infraredDataIndex) {
    lv_obj_t* btn = NULL;
    // 根据 infraredDataIndex 选择正确的按钮
    switch(infraredDataIndex) {
        case 0: btn = ui->IR_PAGE_list_IR_item0; break;
        case 1: btn = ui->IR_PAGE_list_IR_item1; break;
        case 2: btn = ui->IR_PAGE_list_IR_item2; break;
        case 3: btn = ui->IR_PAGE_list_IR_item3; break;
        case 4: btn = ui->IR_PAGE_list_IR_item4; break;
        case 5: btn = ui->IR_PAGE_list_IR_item5; break;
        case 6: btn = ui->IR_PAGE_list_IR_item6; break;
        case 7: btn = ui->IR_PAGE_list_IR_item7; break;
        case 8: btn = ui->IR_PAGE_list_IR_item8; break;
        default:
            printf("无效的 infraredDataIndex: %d\n", infraredDataIndex);
            return;
    }

    if (btn) {
        lv_list_focus_btn(ui->IR_PAGE_list_IR,btn);
    }
}

/**
 * @brief 更新音量条
 * @param volume 音量
 */

void update_volume_bar(int volume) {
    // 假设volume的范围是0-100
    lv_bar_set_value((&guider_ui)->AUDIO_PAGE_sound, volume, LV_ANIM_ON);
}
/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP时间在消息的前48字节中
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets

time_t getNtpTime() {
  Udp.begin(localPort);
  int retryCount = 0;
  const int maxRetries = 5;

  while (retryCount < maxRetries) {
    IPAddress ntpServerIP; // NTP服务器的IP地址
    while (Udp.parsePacket() > 0); // 清除任何现有的数据包
    Serial.print("Resolving NTP server: ");
    Serial.println(ntpServerName);
    if (!WiFi.hostByName(ntpServerName, ntpServerIP)) {
      Serial.println("DNS解析失败。");
      delay(100); // 等待1秒再重试
      retryCount++;
      continue;
    }
    Serial.print("NTP服务器IP: ");
    Serial.println(ntpServerIP);
    sendNTPpacket(ntpServerIP);
    uint32_t beginWait = millis();
    while (millis() - beginWait < 5000) {
      int size = Udp.parsePacket();
      if (size >= NTP_PACKET_SIZE) {
        Serial.println("收到NTP响应");
        Udp.read(packetBuffer, NTP_PACKET_SIZE); // 读取数据包到缓冲区
        // 将从位置40开始的四个字节转换为长整数
        unsigned long secsSince1900 = (unsigned long)packetBuffer[40] << 24;
        secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
        secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
        secsSince1900 |= (unsigned long)packetBuffer[43];
        
        if (secsSince1900 != 0) {
          unsigned long epoch = secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
          Serial.print("Unix时间: ");
          Serial.println(epoch);
          return epoch;
        }
      }
    }
    Serial.println("没有NTP响应，重试中...");
    delay(1000); // 等待一会再重试
    retryCount++;
  }
  Serial.println("多次尝试后未能获取NTP时间。");
  return 0; // 如果不成功，返回0或某个错误代码
}

// 向NTP服务器发送请求
void sendNTPpacket(IPAddress &address) {
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011; // LI, Version, Mode
  packetBuffer[1] = 0;          // Stratum, or type of clock
  packetBuffer[2] = 6;          // Polling Interval
  packetBuffer[3] = 0xEC;       // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  Udp.beginPacket(address, 123); // NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

//重置WiFi函数
void wifi_reset() {
  deleteWiFiConfig();
  delay(10);
  Serial.println("重置WiFi成功");
  ESP.restart();
}

// 更新时间函数
void reflashTime() {
  prevDisplay = now();
  digitalClockDisplay();
  prevTime = 0;
}

// 切换天气 or 空气质量
void reflashBanner() { scrollBanner(); }

// 刷新通过WiFi获取的信息
void WIFI_reflash_All() {
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("WIFI CONNECTED");
      getCityWeather();
      getNtpTime();
      //所有需要联网后更新的函数都放在这
      get_honeyed_words();
    } else {
      wifi_state = false;
      goto_clock_page();
    }
}

// 保持lvgl心跳任务
void lvglTask(){
  lv_tick_inc(10);
  lv_task_handler();
}

// 刷新lvgl图表任务
void listTask(){
  if (page = I2S_PAGE)
  {
   list_refresh_falg = true;
  }
}

// 进行音频处理任务
void audioProcessingTask(void *parameter) {
  while (true) {
    if (page == I2S_PAGE) {
      audio_send();
    }
   vTaskDelay(1); // 给其他任务一些时间 
   esp_task_wdt_reset(); // 复位看门狗计时器       
  }
}

// 进行mqtt处理任务
void mqttProcessingTask(void *parameter) {

  uint8_t receivedData[MAX_AUDIO_BUFFER_SIZE];
  size_t bytesReceived;

  while (true) {
    bytesReceived = xStreamBufferReceive(audioStreamBuffer, receivedData, sizeof(receivedData), portMAX_DELAY);

    if (bytesReceived > 0) {
      // 检查最后一个字节是否为 0xFF 并且总长度是否大于1
      if (receivedData[bytesReceived - 1] == 0xFF) {
        i2s_zero_dma_buffer(SPK_I2S_PORT);
        Serial.printf("静音\n");
        bytesReceived--; // 忽略最后一个静音字节
        bytesReceived--; // 忽略最后一个静音字节
      }
      else if (bytesReceived > 0 && bytesReceived <= MAX_AUDIO_BUFFER_SIZE) {
        for (int i = 0; i < bytesReceived; i++) {
          recive_16bit[i] = ((receivedData[i] - 128) << 5) * volume;
          output_16bit[i * 2] = recive_16bit[i];
          output_16bit[i * 2 + 1] = recive_16bit[i];
        }
        I2Swrite(output_16bit, bytesReceived);
        //Serial.print("Length: ");
        Serial.println(bytesReceived);
        list_refresh_falg = true;
      } else if (bytesReceived > MAX_AUDIO_BUFFER_SIZE) {
        Serial.println("接收到的数据超过缓冲区大小");
      }
    }
  }
}

// 更新图表任务
void updateChartTask(void *parameter) {
  while (true) {
    if (page == I2S_PAGE && list_refresh_falg) {
      update_chart_with_i2s_data((&guider_ui)->AUDIO_PAGE_chart_2, recive_16bit_list, 128);
      list_refresh_falg = false;
    }
     if (page == I2S_PAGE && list_refresh_falg_send){
     update_chart_with_i2s_data((&guider_ui)->AUDIO_PAGE_chart_2, samples_16bit, 128);
     list_refresh_falg_send = false;
     }

    vTaskDelay(100); // 给其他任务一些时间  
  }
}

// 删除freertos任务
void deleteTasks() {
  if (audioTaskHandle != NULL) {
    vTaskDelete(audioTaskHandle);
    audioTaskHandle = NULL; // 删除后清空句柄
  }
  if (chartTaskHandle != NULL) {
    vTaskDelete(chartTaskHandle);
    chartTaskHandle = NULL; // 删除后清空句柄
  }
    if (mqttTaskHandle != NULL) {
    vTaskDelete(mqttTaskHandle);
    mqttTaskHandle = NULL; // 删除后清空句柄
  }
}

// 清空屏幕
void clear_screen() {
   tft.fillScreen(bgColor); 
   }

// 守护线程池
void Supervisor_controller() {
  if (controller.shouldRun()) {
    controller.run();
  }
}

void Supervisor_icon_controller() {
  if (reflash_icon.shouldRun()) {
    reflash_icon.run();
  }
}

void Supervisor_miss_controller() {
  if (reflash_miss.shouldRun()) {
    reflash_miss.run();
  }
}

// LVGL任务处理函数
void Supervisor_lvglTask_controller() {
  if(lvgl_thread.shouldRun()){
  lvgl_thread.run();  
  }
}

void Supervisor_listTask_controller() {
  if(list_thread.shouldRun()){
  list_thread.run();  
  }
}

//检查自身是否连接mqtt的freertos任务
void mqttCheckConnectionTask(void *pvParameters) {
  for (;;) {
    //update_onlineStatus();
    if (!mqttClient.connected()) {
      mqtt_reconnect();
    }
    vTaskDelay(pdMS_TO_TICKS(3000)); // 每5秒检查一次
  }
}

// 绑定按钮并初始化任务
void setup_button(){
  Button_right.setDoubleClickTime(500);
  Button_right.setLongClickTime(1000);
  Button_left.setDoubleClickTime(500);                // 双击时间
  Button_left.setLongClickTime(1000);                 // 设置长按时间
  Button_middle.setDoubleClickTime(500);
  Button_middle.setLongClickTime(1000);
  Button_right.setClickHandler(rightsingleclick);
  Button_right.setDoubleClickHandler(rightdoubleclick);
  Button_right.setLongClickHandler(rightlongclick);
  Button_left.setClickHandler(leftsingleclick);       // 单击事件
  Button_left.setDoubleClickHandler(leftdoubleclick); // 双击事件
  Button_left.setLongClickDetectedHandler(leftlongclick); // 长按事件，重启
  Button_middle.setClickHandler(middlesingleclick);
  Button_middle.setDoubleClickHandler(middledoubleclick);
  Button_middle.setLongClickHandler(middlelongclick);
  Button_left.setPressedHandler(leftpressed);
  Button_middle.setPressedHandler(middlepressed);
  Button_right.setPressedHandler(rightpressed);
  Button_right.setReleasedHandler(rightreleased);
  Button_left.setReleasedHandler(leftreleased);
  Button_middle.setReleasedHandler(middlereleased);
  Button_right.setTapHandler(buttonHandler);
  Button_left.setTapHandler(buttonHandler);
  Button_middle.setTapHandler(buttonHandler);


}

const uint16_t *Animate_value; // 指向关键帧的指针

void refresh_AnimatedImage() {
  if (millis() - Amimate_reflash_Time > 30) // x ms切换一次
  {
    tft.setSwapBytes(true); //使图片颜色由RGB->BGR
    // 使用pushImage显示当前帧
    Amimate_reflash_Time = millis();
    imgAnim(&Animate_value,Mode);
    tft.pushImage(160, 160, 80, 80, Animate_value);
    tft.setSwapBytes(false); //使图片颜色由RGB->BGR
  }
}



void refresh_AnimatedIcon() {
      // Display image
    if(play_ye > 1){
    tft.pushImage(30, 30, 180, 180, images[play_ye]);
    play_ye--;
    }
    else
    {
    play_flag = false;
    tft.setSwapBytes(false);
    }
}

void refresh_AnimatedMiss() {
    play_ye++;
    if(play_ye < 18){
    tft.setSwapBytes(true);
    tft.pushImage(100, 30, 40, 40, image_miss[play_ye]);  // Display image
    tft.setSwapBytes(false);
    }
    else
    {
      play_ye = 0;
    }
}

/******************************************************************************
 * 纪念日页面
 ********************************************************************************/

/**
 * 计算纪念日天数  start_time 参数是从在一起的时间-秒
 */
unsigned int get_love_day(time_t start_time) {
  // 获取最新时间
  time_t cur_time = now();
  time_t love_seconds = cur_time - start_time;
  unsigned int day = (love_seconds) / (24 * 3600) + 1;
  return day;
}

/**
 * 显示恋爱时间
 */
// 用快速线方法绘制数字
void drawLineFontForDay(uint32_t _x, uint32_t _y, uint32_t _num, uint32_t _size,
                        uint32_t _color) {
  uint32_t fontSize;
  const LineAtom *fontOne;
  fontOne = middleLineFont[_num];
  fontSize = middleLineFont_size[_num];

  // 绘制前清理字体绘制区域  周围填充4像素
  tft.fillRoundRect(_x - 4, _y - 4, 26, 38, 4, whiteColor);

  for (uint32_t i = 0; i < fontSize; i++) {
    tft.drawFastHLine(fontOne[i].xValue + _x, fontOne[i].yValue + _y,
                      fontOne[i].lValue, _color);
  }
}


//////////////////////////////////////////////////////////////////
// 显示动画
///////////////////////////////////////////////////////////////


/**
 * 显示底部文字
 *
 */
void show_buttom(String since) {
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
 // clk.loadFont(URW_Chancery_L_Medium_Italic_20Bitmaps);
  clk.setFreeFont(&URW_Chancery_L_Medium_Italic_20);
  //clk.loadFont(lonely20);
  clk.createSprite(240, 30);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(Main_descr, bgColor);
  clk.drawString(since, 120, 15);
  clk.pushSprite(0, 210);
  clk.deleteSprite();

  clk.unloadFont();
}

/**
 * 显示情话
 *
 */

// 获取情话

void get_honeyed_words() {
  HTTPClient httpClient;// 创建 HTTPClient 对象
  WiFiClient wifiClient;  
  //httpClient.begin(wifiClient, "https://api.uomg.com/api/rand.qinghua?format=json");
  httpClient.begin(wifiClient, "http://api.uomg.com/api/rand.qinghua?format=json");
  httpClient.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac "
                          "OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) "
                          "Version/11.0 Mobile/15A372 Safari/604.1");
  httpClient.addHeader("Referer", "http://api.uomg.com");
  int httpCode = httpClient.GET();
  Serial.print("Send GET request to URL: ");
  Serial.println("https://api.uomg.com/api/rand.qinghua?format=json");

  if (httpCode == HTTP_CODE_OK) {
    String payload = httpClient.getString();
    Serial.println("Response:");
    Serial.println(payload);
    words = payload;
}
   else {
    Serial.print("HTTP Code: ");
    Serial.println(httpCode);
    Serial.println("获取情话失败...");
    Serial.println(httpClient.getString());
  }

  httpClient.end();
}



// 显示情话

void show_honey_words(String *words) {

  // 解析第一段JSON
  DynamicJsonDocument doc(300);
  deserializeJson(doc, *words);
  JsonObject data = doc.as<JsonObject>();
  String content = data["content"];
  Serial.println(content);
  // 字符串预处理
  content.replace("<br>", " | ");
  content.replace(",", "，");
  content.replace("?", "？");
  content.replace(".", "...");
  content.replace("=", " = ");
  content.replace("(", "（");
  content.replace(")", "）");

  // 去掉顿号
  int index_str = content.indexOf("、");
  if (index_str == -1) {
    index_str = 0;
  } else {
    index_str += 3;
  }

  uint16_t num = 11; // 每行显示多少中文字符
  // 分割字符串，一行 11 个中文字符
  String first_str = content.substring(index_str, index_str + num * 3);
  String second_str = content.substring(index_str + num * 3, index_str + num * 3 * 2);
  String third_str = content.substring(index_str + num * 3 * 2, index_str + num * 3 * 3);
  String forth_str = content.substring(index_str + num * 3 * 3, index_str + num * 3 * 4);

  Serial.println("第一段=" + first_str);
  Serial.println("第二段=" + second_str);
  Serial.println("第三段=" + third_str);
  Serial.println("第四段=" + forth_str);

  // 如果超过四行,这段情话不显示；显示自定义的
  if (forth_str != "") {
    first_str = "哈哈哈，想你拉！";
    second_str = "";
    third_str = "";
  }

  // 如果只有一行，那么居中显示，（显示在第二行）
  if (second_str == "") {
    second_str = first_str;
    first_str = "";
  }

  // 显示情话
  sdm.setColorDepth(16);
  sdm.loadFont(lonely20);

  // 第一行
  sdm.createSprite(240, 20);
  sdm.fillSprite(bgColor);
  sdm.setTextWrap(false);
  sdm.setTextDatum(CC_DATUM);
  sdm.setTextColor(hourColor, bgColor);
  sdm.drawString(first_str, 120, 11);
  sdm.pushSprite(0, 153);
  sdm.deleteSprite();

  // 第二行
  sdm.createSprite(240, 20);
  sdm.fillSprite(bgColor);
  sdm.setTextWrap(false);
  sdm.setTextDatum(CC_DATUM);
  sdm.setTextColor(hourColor, bgColor);
  sdm.drawString(second_str, 120, 11);
  sdm.pushSprite(0, 173);
  sdm.deleteSprite();

  // 第三行
  sdm.createSprite(240, 20);
  sdm.fillSprite(bgColor);
  sdm.setTextWrap(false);
  sdm.setTextDatum(CC_DATUM);
  sdm.setTextColor(hourColor, bgColor);
  sdm.drawString(third_str, 120, 11);
  sdm.pushSprite(0, 193);
  sdm.deleteSprite();

  sdm.unloadFont();
}

void screenTask(){
    if (screenOff_flag && !screenOff) {
    if(millis() - lastActivityTime > TIMEOUT){
      analogWrite(LCD_BL_PIN, 0);
      screenOff = true;
      Serial.println("屏幕关闭");
    }
  }
}
/*电量显示*/
void Battery() {
  uint16_t batteryLevel = analogRead(battery);
  screenOff_flag = true;
  Serial.println(batteryLevel);
  if (batteryLevel >= 2580) {
    screenOff_flag = false;//充电状态不熄灭屏幕
    analogWrite(LCD_BL_PIN, brightness);
    TJpgDec.drawJpg(185, 77, battery0, sizeof(battery0));
  } else if (batteryLevel >= 2400) {
    TJpgDec.drawJpg(185, 77, battery1, sizeof(battery1));
  }else if (batteryLevel >= 2260) {
    TJpgDec.drawJpg(185, 77, battery1, sizeof(battery2));
  }  else {
    TJpgDec.drawJpg(185, 77, battery3, sizeof(battery3));
  }
}


void change_gif() {
  Animate_key = -1;
Mode = random(1, 5); // 生成0到4的随机数用来随机首页动画
  
}

/**
 * 主页面
 */


unsigned int mqtt_reconn_count =
    0; // 记录Mqtt重连次数，如果重连失败超过5次，就放弃连接；
bool mqtt_conn_flag = true; // 默认是可以重连的；


void mqtt_reconnect() {
  // Loop until we're reonline
  if (!mqttClient.connected() && mqtt_conn_flag) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "test1";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect with username and password
    if (mqttClient.connect(clientId.c_str(), MQTT_USER, MQTT_PASSWORD,PUB_statusTopic, 1, true, "offline")) {
      Serial.println("CONNECTED...");
      mqttClient.subscribe(IN_TOPIC);
      mqttClient.subscribe(LOCALTOPIC,0);
      mqttClient.subscribe(LOCALTOPIC_1,0);
      mqttClient.subscribe(LOCAL_statusTopic,0);
      mqttClient.publish(PUB_statusTopic,"online", true);
      mqttClient.publish(PUB_statusTopic, "request", true);
      Serial.printf("正在请求对方在线的状态\n");
      // 回复重连次数
      mqtt_reconn_count = 0;
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");

      mqtt_reconn_count++;
      if (mqtt_reconn_count > 5)
        mqtt_conn_flag = false; // 放弃连接
                                // Wait 5 seconds before retrying
                                // delay(5000);
    }
  }
}

// 启用震动函数
void ringBell() {
    if (ring_state) {
        for (int i = 0; i < 2; i++) {
            digitalWrite(ring, LOW);
            delay(600);
            digitalWrite(ring, HIGH);
            delay(1000);
        }
    }
}

/**
* @brief mqtt消息处理函数
* @param topic 消息主题
* @param payload 消息内容
* @param length 消息长度
*/

void mqttcallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] -->");
    
    String msg = "";
    for (int i = 0; i < length; i++) {
        msg += (char)payload[i];
    }
    
    Serial.println(msg);

    // 检查是否包含分隔符
    int delimiterIndex = msg.indexOf(',');

    if (delimiterIndex != -1) {
        // 处理带有数值的消息
        String command = msg.substring(0, delimiterIndex);
        couple_miss_count = msg.substring(delimiterIndex + 1);
        preferences.begin("miss", false);
        preferences.putString("couplecount",couple_miss_count);//获取上次我储存的他向我次数
        preferences.end();
        if (command == IN_MSG) {
            tft.setSwapBytes(false);
            play_flag = true;
            pre_page = page;
            goto_miss_page("收到：宝贝，我想你辣！", TFT_WHITE);
            ringBell();
        }
    } else {
        // 处理不带数值的消息
        if (msg == OUT_MSG_CHECK) {
            goto_miss_page("对方已经收到想念信号", TFT_WHITE);
            ringBell();
        }
    }
}


/**
* @brief mqtt音频处理函数
* @param topic 消息主题
* @param payload 消息内容
* @param length 消息长度
*/

void mqttcallback_audio(char *topic, byte *payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] -->");
  String msg = "";
  for (int i = 0; i < length; i++) {
    // Serial.print((char)payload[i]);
    msg += (char)payload[i];
  }
  Serial.println(msg);
  // 收到想你消息
  if(page != MISSU_PAGE)
  pre_page = page;//记录当前页面再切换
  if (msg == IN_MSG) {
    role_1 = RECEIVER; // 角色切换到接受者
    pre_page = page; // 记录当前页面再切换
    goto_miss_page("宝贝,快来连麦", TFT_WHITE); // 切换到想你页面
    ringBell();
  }

  if (msg == OUT_MSG_CHECK) {
    goto_miss_page("收到：对方已经准备好了", TFT_WHITE);
    ringBell();
  }
  if(msg == NO_READY){
    role_1 = RECEIVER; // 角色切换到接受者
    goto_miss_page("宝宝现在没空呢", TFT_RED);
    ringBell();
  }
    
}

/**
* @brief mqtt状态消息处理函数
* @param topic 消息主题
* @param payload 消息内容
* @param length 消息长度
*/

void mqttcallback_status(char *topic, byte *payload, unsigned int length) {
    Serial.println("status:");
    String msg = "";
    for (int i = 0; i < length; i++) {
        msg += (char)payload[i];
    }
    msg.trim();//清除mes前后空格
    
    // 检查接收到的消息
    if (msg == "online") {
        onlineStatus = true; //收到消息设置为在线
        appStatus = false;
        Serial.println("对方设备在线");
    }
    if (msg == "offline"){
        onlineStatus = false;
        appStatus = false;
        Serial.println("对方设备离线");
    }
    if (msg == "apponline"){
        onlineStatus = true; //收到消息设置为在线
        appStatus = true;
        Serial.println("对方已打开app");
    }
    if (msg == "request"){
      if(page == I2S_PAGE && send_flag == true){
        mqttClient.publish(PUB_statusTopic, "apponline", true);
      }
      else
      mqttClient.publish(PUB_statusTopic, "online", true);
    }
    update_onlineStatus();
}

/**
* @brief mqtt消息处理函数
* @param topic 消息主题
* @param payload 消息内容
* @param length 消息长度
*/

void callback(char* topic, byte* payload, unsigned int length) {
 
  if (strcmp(topic, LOCALTOPIC) == 0) {
    if(page == I2S_PAGE)
    audioCallback(topic, payload, length);
  } else if(strcmp(topic, LOCALTOPIC_1) == 0){
    mqtt_flag = "i2s";
    mqttcallback_audio(topic, payload, length);
  }
    else if (strcmp(topic, LOCAL_statusTopic) == 0)
    {
      mqttcallback_status(topic, payload, length);
    }
    else if(strcmp(topic,IN_TOPIC) == 0)
    {
      mqtt_flag = "miss";
      mqttcallback(topic, payload, length);
    }
}

/**
 * 单击按钮
 * 切换页面、切换列表（红外页面）、调整音量（对讲机页面）、接受连麦（邀请连麦页面）
 **/
void leftsingleclick(Button2 &btn) {
Serial.println("left click\n");
  // 如果当前页面不是想你页面,则单击按钮，时钟页面和纪念日页面来回切换
  if (page != MISSU_PAGE) {
    if (page == CLOCK_PAGE) {
   role_1 = RECEIVER;
   role = RECEIVER;
      goto_love_page();
    } else if(page == LOVE_PAGE){
      goto_audio_page();
    }
    else if(page == AUDIO_PAGE3){
      goto_IR_page();
    }
    else if(page == IR_PAGE5 && IR_CHOICE == false){
      goto_clock_page();
    }
    if(page == I2S_PAGE){
   volume = (volume + 1);
   preferences.begin("i2ssets", false);
   preferences.putFloat("volume", volume);
   preferences.end();
   Serial.println("现在的音量是：");
   Serial.print(volume);
   lv_bar_set_value((&guider_ui)->AUDIO_PAGE_sound, volume, LV_ANIM_ON);
    }
   if(page == IR_PAGE5 && IR_CHOICE == true){
       DataIndex++;
    if(DataIndex == 8)
    DataIndex = 0;
    update_focused_item(&guider_ui,DataIndex);
    Serial.println("DataIndex:");
    Serial.println(DataIndex);
  }
  return;
}
if(page == MISSU_PAGE) {
      if(word1== "宝宝现在没空呢"){
      goto_page(pre_page);
      return;
    }
    if(role_1 == SENDER){
    goto_I2S_page();
    return;
    }
    if (role == SENDER) {
    goto_page(LOVE_PAGE);
    // 如果当前在想你页面，而且是发送消息身份，不能再发送消息
    // 可以返回在主页面，双击再发送；
    return;
    }
if(role == RECEIVER && mqtt_flag == "miss"){
    if (mqttClient.connected()) {
      mqttClient.publish(OUT_TOPIC, IN_MSG_CHECK); // 发送收到消息确认
    }
    goto_page(LOVE_PAGE);// 如果是接收者（收到想你消息的一方），单击按钮退出，并且发送收到消息确认消息
    return;
    }
  if (role_1 == RECEIVER && mqtt_flag == "i2s") {
    if (mqttClient.connected()) {
      mqttClient.publish(PUBTOPIC_1 , IN_MSG_CHECK); // 发送收到消息确认
    }
    goto_I2S_page();// 如果是接收者（收到想你消息的一方），单击按钮切换到i2s页面
  }

}

}


/**
 *
 * 双击按钮，发送红外线信号
 * 
 **/
void leftdoubleclick(Button2 &btn) {

  Serial.println("double click\n");
  Serial.print("page is: ");
  Serial.print(page);
  Serial.print(", role is: ");
  Serial.print(role);
    if(IR_CHOICE){
  readAndSend(DataIndex);
  Serial.println("发送\n");
  } 
  
}


void rightsingleclick(Button2 &btn) {
  Serial.println("right single click\n");
      if (page == CLOCK_PAGE) {
      role_1 = RECEIVER;
      role = RECEIVER;
      goto_IR_page();
    } else if(page == LOVE_PAGE){
      goto_clock_page();
    }
    else if(page == AUDIO_PAGE3){
      goto_love_page();
    }
        else if(page == IR_PAGE5 && IR_CHOICE == false){
      goto_audio_page();
    }
  if (page == MISSU_PAGE) {
    if(word1== "宝宝现在没空呢"){
      goto_page(pre_page);
      return;
    }
    if(role_1 == SENDER){
    setup_I2S_lvgl();
    page = I2S_PAGE;
    return;
    }
    if (role == SENDER) {
    goto_page(LOVE_PAGE);
    // 如果当前在想你页面，而且是发送消息身份，不能再发送消息
    // 可以返回在主页面，双击再发送；
    return;
    }
    if(role == RECEIVER && mqtt_flag == "miss"){
    if (mqttClient.connected()) {
      mqttClient.publish(OUT_TOPIC, IN_MSG_CHECK); // 发送收到消息确认
    }
    goto_page(LOVE_PAGE);// 如果是接收者（收到想你消息的一方），单击按钮退出，并且发送收到消息确认消息
    return;
    }
    if (role_1 == RECEIVER && mqtt_flag == "i2s") {
    if (mqttClient.connected()) {
      mqttClient.publish(PUBTOPIC_1 , NO_READY); // 发送拒绝消息
    }
    goto_page(pre_page);// 如果是接收者（收到想你消息的一方），单击按钮切换到原来页面
    return;
    }
}


    if(page == I2S_PAGE){
   volume = (volume - 1.0);
   preferences.begin("i2ssets", false);
   preferences.putFloat("volume", volume);
   preferences.end();
   Serial.println("现在的音量是：");
   Serial.print(volume);
   lv_bar_set_value((&guider_ui)->AUDIO_PAGE_sound, volume, LV_ANIM_ON);//更新屏幕音量显示
   }
if(page == IR_PAGE5 && IR_CHOICE == true){
       DataIndex--;
    if(DataIndex < 0 )
    DataIndex = 0;
    update_focused_item(&guider_ui,DataIndex);
    Serial.println("DataIndex:");
    Serial.println(DataIndex);
  }
  
}

void rightdoubleclick(Button2 &btn) {
  
  Serial.println("right double click\n");
  if(page == I2S_PAGE && role_1 != SENDER){
  Serial.print("page is: ");
  Serial.print(page);
  Serial.print(", role_1 is: ");
  Serial.print(role_1);
  String word = "已发送连麦请求：等待回应";
  uint16_t word_color = TFT_WHITE;
  if (mqttClient.connected()) {
    mqttClient.publish(PUBTOPIC_1, OUT_MSG);
    Serial.println("已发送等待连麦\n");
  } else {
    word = "send fail...";
    word_color = TFT_RED;
  }
  role_1 = SENDER;
  //pre_page = I2S_PAGE; // 跳转之前记录跳转前的页面
  Serial.println("正在跳转\n");
  goto_miss_page(word, word_color);
  lv_obj_del((&guider_ui)->AUDIO_PAGE);
  }
  if(page == IR_PAGE5 && IR_CHOICE)
  {
    lv_event_send((&guider_ui)->IR_PAGE,LV_EVENT_PRESSED, NULL);
    processAndStore(DataIndex);
    delay(1000);
    lv_event_send((&guider_ui)->IR_PAGE,LV_EVENT_RELEASED, NULL);
  }
  
}

void leftlongclick(Button2 &btn) {
  Serial.println("左键长按\n");
  if(page == CLOCK_PAGE)
  {
   ring_state = !ring_state;
   preferences.begin("settings", false);
   preferences.putBool("ring", ring_state); // 0 is the default value
   preferences.end();
   Serial.printf("ring_state:");
   Serial.println(ring_state);
     if(ring_state){
  tft.setSwapBytes(true);
  tft.pushImage(0, 8, 25, 25, zhendong_0);
  tft.setSwapBytes(false);}
  else{
    tft.fillRect(0, 8, 25, 25, bgColor);
  }
  update_onlineStatus();
  }
}

void buttonHandler(Button2& btn) {
  if (screenOff) {
    // 如果屏幕是关闭的，只打开背光
    analogWrite(LCD_BL_PIN, brightness);
    lastActivityTime = millis();
    Button_middle.resetPressedState();
    Button_left.resetPressedState();
    Button_right.resetPressedState();
    screenOff = false;
  } else {
    // 如果屏幕是开启的，更新最后活动时间
    lastActivityTime = millis();
  }
}

void middledoubleclick(Button2 &btn) {
  Serial.println("双击中按键\n");

  if(page == I2S_PAGE)
  {  
     goto_audio_page(); //返回等待进入音频动画页面
     return;
  }
if(page == IR_PAGE5){
      IR_CHOICE = !IR_CHOICE;
      goto_IR_page();
      return;
}
  if(page == AUDIO_PAGE3)
  {
    goto_I2S_page();
    return;
  }
}

void exit_I2S_page(){
     deleteTasks();
     if(I2S_STATE == true){
     Serial.println("卸载i2s\n");
     I2SDeinit();//卸载i2s
     }
     I2S_STATE = false;
     if ((&guider_ui)->AUDIO_PAGE != NULL) {
    lv_obj_del((&guider_ui)->AUDIO_PAGE);
    (&guider_ui)->AUDIO_PAGE = NULL;
}//删除音频页面的lvgl组件
     mqttClient.publish(PUB_statusTopic, "online", true);
}

void middlelongclick(Button2 &btn) {
}

void middlesingleclick(Button2 &btn) { 
Serial.println("单击中按键\n");

if(page == CLOCK_PAGE){
    change_gif();
    }
if (page == MISSU_PAGE) {
      if(word1== "宝宝现在没空呢"){
      goto_page(pre_page);
      return;
    }
    if(role_1 == SENDER){
    setup_I2S_lvgl();
    page = I2S_PAGE;
    return;
    }
    if (role == SENDER) {
    goto_page(LOVE_PAGE);
    // 如果当前在想你页面，而且是发送消息身份，不能再发送消息
    // 可以返回在主页面，双击再发送；
    return;
    }
    if(role == RECEIVER && mqtt_flag == "miss"){
    if (mqttClient.connected()) {
      mqttClient.publish(OUT_TOPIC, IN_MSG_CHECK); // 发送收到消息确认
    }
    goto_page(LOVE_PAGE);// 如果是接收者（收到想你消息的一方），单击按钮退出，并且发送收到消息确认消息
    return;
    }
  }
if(page == AUDIO_PAGE3){
  goto_I2S_page();
    return;
    }
if(page == IR_PAGE5){
  if(IR_CHOICE == false){
    {
     enter_IR_page();
    }
  }
}
if(page == LOVE_PAGE && role != SENDER){
  play_flag = true;
  String word = "已发送：我想你！";
  uint16_t word_color = TFT_WHITE;
  int num = my_miss_count.toInt();
  num += 1;
  my_miss_count = String(num);
  preferences.begin("miss", false);
  preferences.putString("mycount",my_miss_count);//储存的我想她次数
  preferences.end();
  if (mqttClient.connected()) {
  String payload = String(OUT_MSG) + "," + String(my_miss_count);
// 发送 MQTT 消息
  mqttClient.publish(OUT_TOPIC, payload.c_str());
  Serial.println("已发送想你了\n");
  } else {
    word = "send fail...";
    word_color = TFT_RED;
  }
  role = SENDER;
  pre_page = page; // 跳转之前记录跳转前的页面
  Serial.println("正在跳转\n");
  goto_miss_page(word, word_color);
  }
}


void rightlongclick(Button2 &btn) {
  Serial.println("长按右键\n");

    // 4级调光，直接使用PWM值：0, 85, 170, 255
    if (brightness == 255) brightness = 170;
    else if (brightness == 170) brightness = 85;
    else if (brightness == 85) brightness = 45;
    else if (brightness == 45) brightness = 0;
    else brightness = 255;
    analogWrite(LCD_BL_PIN, brightness);
    // 保存设置
    preferences.begin("settings", false);
    preferences.putInt("brightness", brightness);
    preferences.end();
    Serial.print("brightness:");

}

void leftpressed(Button2 &btn){
    if(page == IR_PAGE5 && IR_CHOICE){
    Serial.println("Button pressed, sending LV_EVENT_PRESSED");
    lv_event_send((&guider_ui)->IR_PAGE_ICON_SEND,LV_EVENT_PRESSED, NULL);
    }
        if(page == I2S_PAGE){
    Serial.println("Button pressed, sending LV_EVENT_PRESSED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_sound_high,LV_EVENT_PRESSED, NULL);
    }

}

void middlepressed(Button2 &btn){

  if(page == I2S_PAGE){
    Serial.println("Button pressed, sending LV_EVENT_PRESSED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_icon_ear,LV_EVENT_PRESSED, NULL);
    icon_lastState = 1;
    }
    if(page == IR_PAGE5 && IR_CHOICE){
      Serial.println("Button PRESSED, sending LV_EVENT_PRESSED");
      lv_event_send((&guider_ui)->IR_PAGE_ICON_BACK,LV_EVENT_PRESSED, NULL);
    }

}

void rightpressed(Button2 &btn){
  if(page == CLOCK_PAGE){
      if (Button_left.isPressed()) {
   tft.setSwapBytes(false);
   wifi_reset();
  }
  }
  if(page == IR_PAGE5 && IR_CHOICE){
  Serial.println("Button pressed, sending LV_EVENT_PRESSED");
  lv_event_send((&guider_ui)->IR_PAGE_ICON_REC,LV_EVENT_PRESSED, NULL);
  }
  if(page == I2S_PAGE){
    Serial.println("Button pressed, sending LV_EVENT_PRESSED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_sound_low,LV_EVENT_PRESSED, NULL);
    }
      
}

void leftreleased(Button2 &btn){

    if(page == IR_PAGE5 && IR_CHOICE){
    Serial.println("Button pressed, sending LV_EVENT_RELEASED");
    lv_event_send((&guider_ui)->IR_PAGE_ICON_SEND,LV_EVENT_RELEASED, NULL);
    }
            if(page == I2S_PAGE){
    Serial.println("Button pressed, sending LV_EVENT_RELEASED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_sound_high,LV_EVENT_RELEASED, NULL);
    }
    
}

void middlereleased(Button2 &btn){

  if(page == I2S_PAGE ){
    Serial.println("Button released, sending LV_EVENT_RELEASED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_icon_ear, LV_EVENT_RELEASED, NULL);
    icon_lastState = 0;
    }
    if(page == IR_PAGE5 && IR_CHOICE){
      Serial.println("Button RELEASED, sending LV_EVENT_PRESSED");
      lv_event_send((&guider_ui)->IR_PAGE_ICON_BACK,LV_EVENT_RELEASED, NULL);
    }
    
}


void rightreleased(Button2 &btn){

  if(page == IR_PAGE5 && IR_CHOICE){
  Serial.println("Button pressed, sending LV_EVENT_PRESSED");
  lv_event_send((&guider_ui)->IR_PAGE_ICON_REC,LV_EVENT_RELEASED, NULL);
  }
  if(page == I2S_PAGE){
    Serial.println("Button pressed, sending LV_EVENT_PRESSED");
    lv_event_send((&guider_ui)->AUDIO_PAGE_sound_low,LV_EVENT_RELEASED, NULL);
  }
  
}

/**
 * @brief 切换到想你页面函数
 * @param word: 显示的文字
 * @param word_color: 显示的文字颜色
 **/
void goto_miss_page(String word, uint16_t word_color) {
  // 切换到 纪念日页面
  Serial.println("正在切换到想你页面....");
  tft.setSwapBytes(false);
  clear_screen(); // 清屏
  page = MISSU_PAGE;
  tft.fillScreen(Mint_Green);
  show_miss_page(word, word_color);
  Serial.println("已切换到想你页面");
}

/**
 * 切换到时钟页面
 **/
void goto_clock_page() {
  tft.setSwapBytes(false);
  role = RECEIVER;
  // WiFi.forceSleepWake(); // wifi on
  clear_screen();                                             // 清屏
  page = CLOCK_PAGE;                                          // 记录页面
  TJpgDec.drawJpg(15, 183, temperature, sizeof(temperature)); // 温度图标
  TJpgDec.drawJpg(15, 213, humidity, sizeof(humidity));       // 湿度图标
  if(ring_state){
  tft.setSwapBytes(true);
  tft.pushImage(0, 8, 25, 25, zhendong_0);
  tft.setSwapBytes(false);}
  if(wifi_state == false){
  tft.setSwapBytes(true);
  tft.pushImage(30,0, 15, 15, wifistate);
  tft.setSwapBytes(false);
  }
  Battery();
  digitalClockDisplay(1); // 强制刷新显示时间
  weaterData(&(weather_msg.cityDZ), &(weather_msg.dataSK), &(weather_msg.dataFC)); // 显示缓存温湿度缓存值
  update_onlineStatus();//更新在线状态
  Serial.println("已切换到时钟页面");
}

/**
 * //切换到 纪念日页面
 */



void drawThickCurvedDashedLine(int x0, int y0, int x1, int y1, int cx, int cy, uint16_t color, int dashLength, int gapLength, int thickness) {
  float lastX = x0;
  float lastY = y0;
  int dashCount = 0;
  bool drawing = true;
  for (float t = 0; t <= 1; t += 0.02) {
    float xt = pow(1-t, 2) * x0 + 2 * (1-t) * t * cx + pow(t, 2) * x1;
    float yt = pow(1-t, 2) * y0 + 2 * (1-t) * t * cy + pow(t, 2) * y1;
    if (drawing) {
      // 使用drawThickLine代替简单的drawLine
      for (int i = -thickness/2; i <= thickness/2; i++) {
        tft.drawLine(lastX, lastY + i, xt, yt + i, color);
      }
      dashCount++;
      if (dashCount >= dashLength) {
        drawing = false;
        dashCount = 0;
      }
    } else {
      dashCount++;
      if (dashCount >= gapLength) {
        drawing = true;
        dashCount = 0;
      }
    }
    lastX = xt;
    lastY = yt;
  }
}


void goto_love_page() {
  int day = get_love_day(love_start_time);//获取在一起天数
  clear_screen();                         // 清屏
  page = LOVE_PAGE;                       // 记录页面
  tft.fillScreen(bgColor);                // 填充背景粉色 tft.color565(255,182,193)
  drawTwoCircularImages(30,30,20,MY_profilphoto,40,40,210,123,20,COUPLE_profilphoto,40,40,bgColor);//绘制两个头像
  tft.fillRoundRect(60, 48, 117, 57, 29, tft.color565(240,124,130)); // 绘制粉色的圆角矩形
  tft.fillRoundRect(65, 53, 107, 47, 25, TFT_WHITE);                 // 绘制白色的圆角矩形
  clk.setColorDepth(16);        //设置颜色深度
  clk.loadFont(ARLRDBD28);      //加载字体
  clk.createSprite(66, 30);     //创建空间
  clk.fillSprite(TFT_WHITE);    //填充白色
  clk.setTextWrap(false);       //设置文本不换行
  clk.setTextDatum(CC_DATUM);   //设置文本对齐方式
  clk.setTextColor(tft.color565(238,63,77), TFT_WHITE);  //设置文本颜色
  clk.drawString(String(day), 33, 15);                   //绘制在一起天数
  clk.pushSprite(85,70);                                 //显示这个空间
  clk.deleteSprite();                                    //删除这个空间
     // 在一起多少天
  clk.unloadFont();                                      //卸载字体
  clk.loadFont(heiti12);                                 //加载字体
  clk.createSprite(60, 12);                              //创建空间
  clk.fillSprite(TFT_WHITE);                             //填充白色
  clk.setTextWrap(false);                                //设置文本不换行
  clk.setTextDatum(CC_DATUM);                            //设置文本对齐方式
  clk.setTextColor(TFT_BLACK, TFT_WHITE);                //设置文本颜色
  clk.drawString("在一起已经", 30, 6);                    //绘制文字
  clk.pushSprite(90, 55);                                //显示这个空间
  clk.deleteSprite();                                    //删除这个空间
  clk.createSprite(12, 12);
  clk.fillSprite(TFT_WHITE);
  clk.setTextWrap(false);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_BLACK, TFT_WHITE);
  clk.drawString("天", 6, 6);
  clk.pushSprite(154, 80);
  clk.deleteSprite();
  clk.unloadFont();
  drawThickCurvedDashedLine(15, 44, 64, 67, -10, 80, whiteColor,2,2,1);    // 绘制虚线曲线
  drawThickCurvedDashedLine(175, 87, 225, 113, 250, 80, whiteColor,2,2,1); // 绘制虚线曲线
  show_honey_words(&words);  // 显示情话          
  show_buttom(BUTTOM_WORD);  // 显示底部纪念日
  Serial.println("已切换到纪念日页面");
}


/**
 * 切换到对讲机页面
 **/
void goto_audio_page() {
  page = AUDIO_PAGE3;
  exit_I2S_page();
  // WiFi.forceSleepWake(); // wifi on
  clear_screen();                                             // 清屏
  tft.fillScreen(TFT_WHITE); 
  tft.setSwapBytes(true);
  play_flag = true;
  play_ye = 19;
  //if(play_flag)
  //tft.setSwapBytes(false);
  page = AUDIO_PAGE3;
  Serial.println("已切换到对讲机页面");

}

// 切换到I2S页面并初始化
void goto_I2S_page() {
  page = I2S_PAGE;
  preferences.begin("i2ssets", false);
  volume = preferences.getFloat("volume", 3.0); // 3.0 is the default value
  preferences.end();
  //mqttQueue = xQueueCreate(10, sizeof(byte*));
    if(I2S_STATE == false){
    I2SInit();
    Serial.println("初始化i2s\n");
    }
    audioStreamBuffer = xStreamBufferCreate(STREAM_BUFFER_SIZE, 1);
    I2S_STATE = true;
    page = I2S_PAGE;
     tft.setSwapBytes(false);
    xTaskCreatePinnedToCore(
    audioProcessingTask,   // 任务函数
    "AudioTask",           // 任务名称
    4096,                 // 堆栈大小
    NULL,                  // 任务参数
    0,                     // 任务优先级
    &audioTaskHandle,      // 任务句柄
    0                      // 在核心 0 上运行
  );

  xTaskCreatePinnedToCore(
    updateChartTask,       // 任务函数
    "ChartTask",           // 任务名称
    4096,                 // 堆栈大小
    NULL,                  // 任务参数
    0,                     // 任务优先级
    &chartTaskHandle,      // 任务句柄
    1                      // 在核心 1 上运行
  );
    xTaskCreatePinnedToCore(                                
    mqttProcessingTask,
    "MQTTTask",
    4096*2,
    NULL,
    1,
    &mqttTaskHandle,
    0
  );
    currentBtnState = Button_middle.isPressed();
    mqttClient.publish(PUB_statusTopic, "apponline", true);
    setup_I2S_lvgl();
}

// 初始化对讲机页面的lvgl
void setup_I2S_lvgl() {
    setup_scr_AUDIO_PAGE(&guider_ui);
    lv_setup_AUDIO_PAGE_chart_2(&guider_ui);
    update_volume_bar(volume);
    lv_scr_load((&guider_ui)->AUDIO_PAGE);
    lv_obj_set_event_cb((&guider_ui)->AUDIO_PAGE_icon_ear, imgbtn_event_cb);//按钮回调
    lv_obj_set_event_cb((&guider_ui)->AUDIO_PAGE_connect_icon, imgbtn_event_cb);//按钮回调
    lv_obj_set_event_cb((&guider_ui)->AUDIO_PAGE_sound_high, imgbtn_event_cb);//按钮回调
    lv_obj_set_event_cb((&guider_ui)->AUDIO_PAGE_sound_low, imgbtn_event_cb);//按钮回调
    if(role_1 == SENDER)
    lv_obj_set_hidden((&guider_ui)->AUDIO_PAGE_invate, true);
    update_onlineStatus();
}

/**
 * //红外页面
 */

//切换到红外页面
void goto_IR_page() {
  page = IR_PAGE5;
  if(IR_CHOICE){
    enter_IR_page();
  }
  else{
   exit_IR_page();
  }
}

// 退出红外页面
void exit_IR_page() {
      IR_CHOICE = false;
      if ((&guider_ui)->IR_PAGE != NULL) {
    lv_obj_del((&guider_ui)->IR_PAGE);
    (&guider_ui)->IR_PAGE = NULL;
}
      clear_screen();                                             // 清屏
      tft.fillScreen(TFT_WHITE); 
      tft.setSwapBytes(true);
      tft.pushImage(30, 30, 180, 180, IR_ICON_0);
      tft.setSwapBytes(false);
      page = IR_PAGE5;
      Serial.println("已切换到红外页面");
}

// 进入红外页面并初始化
void enter_IR_page() {
 Serial.println("红外已就绪\n");
    setup_scr_IR_PAGE((&guider_ui));//初始化屏幕
    lv_scr_load((&guider_ui)->IR_PAGE);
    lv_obj_set_event_cb((&guider_ui)->IR_PAGE_ICON_SEND, imgbtn_event_cb);//按钮回调
    lv_obj_set_event_cb((&guider_ui)->IR_PAGE_ICON_REC, imgbtn_event_cb);//按钮回调
    lv_obj_set_event_cb((&guider_ui)->IR_PAGE_ICON_BACK, imgbtn_event_cb);//按钮回调
    update_focused_item(&guider_ui,DataIndex);
     for(char i = 0;i<7;i++)
    {
    String protocolStr;
    char description[100];
    description[0] = 0;
    readData(i, &protocolStr,description); 
        if (protocolStr[0] != '\0') {
        update_lvgl(&guider_ui, i, description, protocolStr.c_str());
      }
    }
    printf("更新完成");
    IR_CHOICE = true;
}

// 切换制定页面
void goto_page(byte page) {
  switch (page) {
  case CLOCK_PAGE:
    goto_clock_page();
    break;
  case LOVE_PAGE:
    goto_love_page();
    break;
  case AUDIO_PAGE3:
     goto_audio_page();
     break;
  case I2S_PAGE:
    goto_I2S_page();
      break;
  case IR_PAGE5:
    goto_IR_page();
      break;
    default:
    Serial.println("错误");
    break;
  }
}

// 更新对方在线状态
void update_onlineStatus(){
  if(page == CLOCK_PAGE){
  if(onlineStatus){
    tft.fillCircle(8,8,4,TFT_GREENYELLOW);
  }
  else{
    tft.fillCircle(8,8,4,TFT_GOLD);
  }
}
    if(page == I2S_PAGE){
      if(appStatus){
  lv_event_send((&guider_ui)->AUDIO_PAGE_connect_icon,LV_EVENT_PRESSED, NULL);
  }
  else{
  lv_event_send((&guider_ui)->AUDIO_PAGE_connect_icon,LV_EVENT_RELEASED, NULL);
      }
 }
}


void setup() {
  Serial.println("开始初始化");
    // 设置按钮回调函数
  Serial.begin(115200);
  Serial.println("初始化震动马达");
  pinMode(ring, OUTPUT);//初始化震动马达
  digitalWrite(ring, HIGH);//设置震动马达关闭
  initIR();    //初始化红外模块
  setTime(0, 0, 0, DAY, MONTH, YEAR);//设置纪念日时间
  love_start_time = now();
  preferences.begin("settings", false);
  brightness = preferences.getInt("brightness", 255); // 获取上次保存的屏幕据亮度信息
  if(brightness == 0) {
  brightness = 45;
  preferences.putInt("brightness", brightness);
  }
  ring_state = preferences.getBool("ring", false);   // 获取上次保存的是否开启震动
  preferences.end();
  preferences.begin("miss", false);
  couple_miss_count = preferences.getString("couplecount","?");//获取情侣已想你次数
  my_miss_count = preferences.getString("mycount","0");
  preferences.end();
  Serial.println("调节led背光");
  pinMode(LCD_BL_PIN, OUTPUT);//初始化屏幕亮度
  analogWrite(LCD_BL_PIN, brightness);
  Serial.println("初始化oled");
  tft.init();//初始化oled
  tft.invertDisplay(1); //反转所有显示颜色：1反转，0正常
  tft.setRotation(LCD_Rotation);//设置屏幕方向
  tft.fillScreen(TFT_BLACK);//设置背景颜色
  tft.setTextColor(TFT_BLACK, bgColor);
  TJpgDec.setJpgScale(1);//设置图片缩放
  TJpgDec.setSwapBytes(true);
  TJpgDec.setCallback(tft_output);
  Serial.println("载入wifi信息");
  loadWiFiInfo();//载入wifi信息
  tryConnectWiFi();//联网失败则打开配网
  configureTopics();//配置mqtt主题等
  while(loadNum < 194 & connectTimes <= 189){ //让动画走完
    loading(1);
    connectTimes = 0;
  }
  Serial.print("本地IP： ");
  Serial.println(WiFi.localIP());
  Serial.print("检查更新： ");
   /* if (checkForUpdates()) {
    Serial.println("有可用更新, starting OTA...");
    updateBin();
  } else {
    Serial.println("无可用更新");
  }*/
  Serial.println("启动UDP");
  //Udp.begin(localPort);
  Serial.println("等待同步...");
  get_honeyed_words();//获取情话
  Serial.println("获取服务器时间");
  setSyncProvider(getNtpTime);//获取服务器时间
  setSyncInterval(300);
  Mode = random(1, 5); // 生成1到4的随机数用来随机首页动画
  Serial.println("屏幕图标初始化");
  tft.fillScreen(bgColor);                                  // 清屏
  TJpgDec.drawJpg(15, 183, temperature, sizeof(temperature)); // 温度图标
  TJpgDec.drawJpg(15, 213, humidity, sizeof(humidity));       // 湿度图标
//屏幕显示是否开启震动
  if(ring_state){ 
  tft.setSwapBytes(true);
  tft.pushImage(0, 8, 25, 25, zhendong_0);
  tft.setSwapBytes(false);}
  clk.setColorDepth(16);
  clk.setSwapBytes(true);
  getCityWeather();//获取天气信息
  Battery();//电量显示
  setup_button(); // 初始化按钮
  //lvgl配置
    lv_init();
    /*Initialize the display*/
    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 240;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);
    LV_THEME_DEFAULT_INIT(LV_THEME_DEFAULT_COLOR_PRIMARY, LV_THEME_DEFAULT_COLOR_SECONDARY,
		LV_THEME_MATERIAL_FLAG_LIGHT, LV_THEME_DEFAULT_FONT_SMALL,  LV_THEME_DEFAULT_FONT_NORMAL,
		LV_THEME_DEFAULT_FONT_SUBTITLE, LV_THEME_DEFAULT_FONT_TITLE);
    init_scr_del_flag(&guider_ui);
    
  // 检查是否连接mqtt的freertos设置
  mqttClient.setServer(MQTT_IP, PORT);
  mqttClient.setCallback(callback);
    xTaskCreatePinnedToCore(
    mqttCheckConnectionTask,  // 任务函数
    "MQTTCheckTask",          // 任务名称
    4096,                     // 堆栈大小
    NULL,                     // 任务参数
    2,                        // 任务优先级
    NULL,                     // 任务句柄设为NULL
    0                         // 在核心0上运行
  );

//////////////////////以下为其他线程初始化////////////////////////////
  //电池状态更新线程
  screen_thread.setInterval(30 * TMS);
  screen_thread.onRun(screenTask);
  // 时间更新线程
  reflash_time.setInterval(300); // 设置所需间隔 300毫秒
  reflash_time.onRun(reflashTime);//
  //电池状态更新线程
  reflash_Battery.setInterval(60 * TMS);
  reflash_Battery.onRun(Battery);
  //天气信息栏刷新线程
  reflash_Banner.setInterval(2 * TMS); // 设置所需间隔 2秒
  reflash_Banner.onRun(reflashBanner);
  //联网刷新信息线程
  reflash_DataFromNetwork.setInterval(5 * 60 * TMS); // 设置所需间隔 5分钟
  reflash_DataFromNetwork.onRun(WIFI_reflash_All);      // WIFI应用
  //时钟页面右下角动画刷新线程
  reflash_Animate.setInterval(TMS / 10); // 设置帧率
  reflash_Animate.onRun(refresh_AnimatedImage);
  controller.run();                      // 启动一次线程池
  // 红外页面动画刷新线程
  reflash_icon.setInterval(100);
  reflash_icon.onRun(refresh_AnimatedIcon);
  // 想你页面动画刷新线程
  reflash_miss.setInterval(100);
  reflash_miss.onRun(refresh_AnimatedMiss);
  // lvgl 更新线程
  lvgl_thread.setInterval(10);
  lvgl_thread.onRun(lvglTask);
  list_thread.setInterval(100);
  list_thread.onRun(listTask);
  lastActivityTime = millis();//屏幕亮起开始计时*/
}


void loop() {
  Button_right.loop();    // 按钮轮询.
  Button_left.loop();     // 按钮轮询.
  Button_middle.loop();   // 按钮轮询.
  mqttClient.loop();      //mqtt信息处理
  if(screen_thread.shouldRun()){
        screen_thread.run();
    }//屏幕亮度控制
  if (page == CLOCK_PAGE) {//时钟页面
    Supervisor_controller(); // 守护线程池
  } else if (page == LOVE_PAGE) {

  } else if (page == MISSU_PAGE) {//想你页面
    if (word1 == "已发送：我想你！"||word1 == "收到：宝贝，我想你辣！"){
    Supervisor_miss_controller();
    }
  }
   else if (page == AUDIO_PAGE3){//对讲机图标页面
     Supervisor_icon_controller();
   }
  else if (page == I2S_PAGE)//对讲机页面
  { 
    Supervisor_lvglTask_controller(); 
  }
    else if (page == IR_PAGE5)//红外页面
  {
    if(IR_CHOICE)//判断是否进入红外页面再判断是否开启循环
    Supervisor_lvglTask_controller(); 
  }
}